package com.nf.practice.streamlambdatest;

import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

public class UserService {

    private org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 获取用户列表
     */
    public static List<User> getUserList() {
        List<User> userList = new ArrayList<User>();
        userList.add(new User(1, "测试01", "男", 32, "研发部", BigDecimal.valueOf(1600)));
        userList.add(new User(2, "测试02", "男", 30, "财务部", BigDecimal.valueOf(1800)));
        userList.add(new User(3, "测试03", "女", 20, "人事部", BigDecimal.valueOf(1700)));
        userList.add(new User(4, "测试04", "男", 38, "研发部", BigDecimal.valueOf(1500)));
        userList.add(new User(5, "测试05", "女", 25, "财务部", BigDecimal.valueOf(1200)));
        return userList;
    }

    /**
     * 使用forEach()遍历列表信息
     * @author pan_junbiao
     */
    @Test
    public void forEachTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();
        System.out.println(userList);

        //方法1 集合类的通用遍历方式, 从很早的版本就有, 用迭代器迭代
        System.out.println("list集合通用遍历方式：");
        Iterator it1 = userList.iterator();
        while(it1.hasNext()){
            System.out.println(it1.next());
        }
        System.out.println("====================================");

        //方法2 集合类的通用遍历方式, 从很早的版本就有, 用迭代器迭代
        System.out.println("list集合迭代器遍历：");
        for(Iterator it2 = userList.iterator();it2.hasNext();){
            System.out.println(it2.next());
        }
        System.out.println("====================================");

        //方法3 增强型for循环遍历
        System.out.println("list集合使用增强型for循环遍历：");
        for(User value : userList){
            System.out.println(value);
        }
        System.out.println("====================================");

        //方法4 一般型for循环遍历
        System.out.println("list集合使用一般型for循环遍历：");
        for(int i = 0;i < userList.size(); i ++){
            System.out.println(userList.get(i));
        }
        System.out.println("====================================");

        //方法5 使用java8新特性遍历用户列表
        System.out.println("list集合使用使用java8新特性遍历用户列表：");
        userList.forEach(System.out::println);
        System.out.println("====================================");
        userList.forEach(user -> {System.out.println(user);});
    }

    /**
     * 使用filter()过滤列表信息
     * @author pan_junbiao
     */
    @Test
    public void filterTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //获取部门为“研发部”的用户列表
        userList = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.toList());

        //遍历用户列表
        userList.forEach(System.out::println);
    }

    /**
     * 使用findAny()获取第一条数据
     * @author pan_junbiao
     */
    @Test
    public void findAnytTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //获取用户名称为“pan_junbiao的博客_02”的用户信息，如果没有找到则返回null
        User user = userList.stream().filter(u -> u.getName().equals("测试02")).findAny().orElse(null);

        //打印用户信息
        System.out.println(user);
    }

    //    注意：findFirst() 和 findAny() 都是获取列表中的第一条数据，
    //    但是findAny()操作，返回的元素是不确定的，
    //    对于同一个列表多次调用findAny()有可能会返回不同的值。
    //    使用findAny()是为了更高效的性能。
    //    如果是数据较少，串行地情况下，一般会返回第一个结果，
    //    如果是并行（parallelStream并行流）的情况，那就不能确保是第一个。
    @Test
    public void findAnytTest2() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        List<User> users = userList.parallelStream().filter(u -> u.getName().startsWith("测")).collect(Collectors.toList());
        users.forEach(System.out::println);
        System.out.println("========================");
        //parallelStream方法能生成并行流，使用findAny返回的不一定是第一条数据
        User user = userList.parallelStream().filter(u -> u.getName().startsWith("测")).findAny().orElse(null);

        //打印用户信息
        System.out.println(user);
    }

    /**
     * 使用map()获取列元素
     * @author pan_junbiao
     */
    @Test
    public void mapTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //获取用户名称列表
        List<String> nameList = userList.stream().map(User::getName).collect(Collectors.toList());
        //或者：List<String> nameList = userList.stream().map(user -> user.getName()).collect(Collectors.toList());

        //遍历名称列表
        nameList.forEach(System.out::println);

        System.out.println("=======================================");
        // 返回的结果为数组类型，写法如下：
        //数组类型
        String[] nameArray = userList.stream().map(User::getName).collect(Collectors.toList()).toArray(new String[userList.size()]);
        System.out.println(Arrays.toString(nameArray));
    }

    /**
     * 使用flatMap()将流中的每一个元素连接成为一个流
     * @author pan_junbiao
     */
    @Test
    public void flatMapTest() {
        //创建城市
        List<String> cityList = new ArrayList<String>();
        cityList.add("北京；上海；深圳；");
        cityList.add("广州；武汉；杭州；");

        //分隔城市列表，使用 flatMap() 将流中的每一个元素连接成为一个流。
        cityList = cityList.stream()
                .map(city -> city.split("；"))
                .flatMap(Arrays::stream)
                .collect(Collectors.toList());

        //遍历城市列表
        cityList.forEach(System.out::println);
    }

    /**
     * 使用distinct()去除重复数据
     * @author pan_junbiao
     */
    @Test
    public void distinctTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //获取部门列表，并去除重复数据
        List<String> departmentList = userList.stream().map(User::getDepartment).distinct().collect(Collectors.toList());

        //遍历部门列表
        departmentList.forEach(System.out::println);
    }

    /**
     * limit(long n)方法用于返回前n条数据
     * skip(long n)方法用于跳过前n条数据
     * @author pan_junbiao
     */
    @Test
    public void limitAndSkipTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //获取用户列表，要求跳过第1条数据后的前3条数据
        userList = userList.stream()
                .skip(1)
                .limit(3)
                .collect(Collectors.toList());

        //遍历用户列表
        userList.forEach(System.out::println);
    }

    /**
     * 使用 anyMatch()、allMatch()、noneMatch() 进行判断
     * @author pan_junbiao
     */
    @Test
    public void matchTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //判断用户列表中是否存在名称为“pan_junbiao的博客_01”的数据
        boolean result1 = userList.stream().anyMatch(user -> user.getName().equals("pan_junbiao的博客_01"));

        //判断用户名称是否都包含“pan_junbiao的博客”字段
        boolean result2 = userList.stream().allMatch(user -> user.getName().contains("pan_junbiao的博客"));

        //判断用户名称是否存在不包含“pan_junbiao的博客”字段
        boolean result3 = userList.stream().noneMatch(user -> user.getName().contains("pan_junbiao的博客"));

        //打印结果
        System.out.println(result1);
        System.out.println(result2);
        System.out.println(result3);
    }

    /**
     * 使用 reduce() 方法
     * @author pan_junbiao
     */
    @Test
    public void reduceTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //用户列表中年龄的最大值、最小值、总和
        int maxVal = userList.stream().map(User::getAge).reduce(Integer::max).get();
        int minVal = userList.stream().map(User::getAge).reduce(Integer::min).get();
        int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum);

        //打印结果
        System.out.println("最大年龄：" + maxVal);
        System.out.println("最小年龄：" + minVal);
        System.out.println("年龄总和：" + sumVal);
    }

    /**
     * 使用 counting() 或 count() 统计
     * @author pan_junbiao
     */
    @Test
    public void countTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //统计研发部的人数，使用 counting()方法进行统计
        Long departCount = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.counting());

        //统计30岁以上的人数，使用 count()方法进行统计（推荐）
        Long ageCount = userList.stream().filter(user -> user.getAge() >= 30).count();

        //统计薪资大于1500元的人数
        Long salaryCount = userList.stream().filter(user -> user.getSalary().compareTo(BigDecimal.valueOf(1500)) == 1).count();

        //打印结果
        System.out.println("研发部的人数：" + departCount + "人");
        System.out.println("30岁以上的人数：" + ageCount + "人");
        System.out.println("薪资大于1500元的人数：" + salaryCount + "人");


        //计算年龄总和
        int sumAge = userList.stream().collect(Collectors.summingInt(User::getAge));
        System.out.println("年龄总和为：" + sumAge);

        //计算平均年龄
        double aveAge = userList.stream().collect(Collectors.averagingDouble(User::getAge));
        System.out.println("平均年龄为：" + aveAge);

    }

    /**
     * 使用 summarizingInt 统计
     * @author pan_junbiao
     */
    @Test
    public void summarizingIntTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //获取IntSummaryStatistics对象
        IntSummaryStatistics ageStatistics = userList.stream().collect(Collectors.summarizingInt(User::getAge));

        //统计：最大值、最小值、总和、平均值、总数
        System.out.println("最大年龄：" + ageStatistics.getMax());
        System.out.println("最小年龄：" + ageStatistics.getMin());
        System.out.println("年龄总和：" + ageStatistics.getSum());
        System.out.println("平均年龄：" + ageStatistics.getAverage());
        System.out.println("员工总数：" + ageStatistics.getCount());
    }

    /**
     * BigDecimal类型的统计
     * @author pan_junbiao
     */
    @Test
    public void BigDecimalTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //最高薪资
        BigDecimal maxSalary = userList.stream().map(User::getSalary).max((x1, x2) -> x1.compareTo(x2)).get();

        //最低薪资
        BigDecimal minSalary = userList.stream().map(User::getSalary).min((x1, x2) -> x1.compareTo(x2)).get();

        //薪资总和
        BigDecimal sumSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add);

        //平均薪资
        BigDecimal avgSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(userList.size()), 2, BigDecimal.ROUND_HALF_UP);

        //打印统计结果
        System.out.println("最高薪资：" + maxSalary + "元");
        System.out.println("最低薪资：" + minSalary + "元");
        System.out.println("薪资总和：" + sumSalary + "元");
        System.out.println("平均薪资：" + avgSalary + "元");
    }

    /**
     * 使用 sorted() 排序
     * @author pan_junbiao
     */
    @Test
    public void sortedTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //根据年龄排序（升序）
        userList = userList.stream().sorted((u1, u2) -> u1.getAge() - u2.getAge()).collect(Collectors.toList());
        //推荐写法：
        //升序：
        // userList = userList.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList());
        //降序：
        // userList = userList.stream().sorted(Comparator.comparingInt(User::getAge).reversed()).collect(Collectors.toList());

        //遍历用户列表
        userList.forEach(System.out::println);
    }

    /**
     * 使用 groupingBy() 分组
     * @author pan_junbiao
     */
    @Test
    public void groupingByTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //根据部门对用户列表进行分组
        Map<String,List<User>> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment));

        //遍历分组后的结果
        userMap.forEach((key, value) -> {
            System.out.println(key + "：");
            value.forEach(System.out::println);
            System.out.println("--------------------------------------------------------------------------");
        });
    }

    /**
     * 使用 groupingBy() 多级分组
     * @author pan_junbiao
     */
    @Test
    public void multGroupingByTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //根据部门和性别对用户列表进行分组
        Map<String,Map<String,List<User>>> userMap = userList.stream()
                .collect(Collectors.groupingBy(User::getDepartment,Collectors.groupingBy(User::getSex)));

        //遍历分组后的结果
        userMap.forEach((key1, map) -> {
            System.out.println(key1 + "：");
            map.forEach((key2,user)->
            {
                System.out.println(key2 + "：");
                user.forEach(System.out::println);
            });
            System.out.println("--------------------------------------------------------------------------");
        });
    }

    /**
     * 使用 groupingBy() 分组汇总
     * @author pan_junbiao
     */
    @Test
    public void groupCollectTest() {
        //获取用户列表
        List<User> userList = UserService.getUserList();

        //根据部门进行分组，汇总各个部门用户的平均年龄
        Map<String, Double> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment, Collectors.averagingInt(User::getAge)));

        //遍历分组后的结果
        userMap.forEach((key, value) -> {
            System.out.println(key + "的平均年龄：" + value);
        });
    }

}
